package net.aiion.weave.impl.routing;

import net.aiion.weave.spi.routing.Route;
import net.aiion.weave.spi.routing.RouteException;

/*
 * URI: /token/token/token.extension
 * 
 * EBNF for a Route:
 * <pre>
 * Route      = Tokens Extension [ Parameters ] [ Fragment ] ;
 * Tokens     = Token { Token } ;
 * Token      = "/" Word ;
 * Extension  = "." Word ;
 * Parameters = "?" Parameter { "&" Parameter }
 * Parameter  = Word "=" Word
 * Fragment   = "#" Word
 * Word       = ? All characters except "/", ".", "?", "=", "&" and "#" ? ;
 * </pre> 
 */
public class RouteBuilder
{
    private final StringBuilder routeTokenBuilder;

    private final String routeURI;
    private int routeURIIndex;

    public RouteBuilder(final String aRouteURI) {
        routeURI = aRouteURI;
        routeTokenBuilder = new StringBuilder();
    }

    public Route createRoute() {
        if (!hasMore()) {
            throw new RouteException("Not enough tokens left");
        }

        final RouteImpl route = new RouteImpl();

        while (hasMore()) {
            routeTokenBuilder.setLength(0);

            final char c = next();

            switch (c) {
            case '/':
                route.addToken(nextToken());
                break;
            case '.':
                route.setExtension(nextExtension());
                break;
            default:
                throw new RouteException("Invalid URI: Unknown character '" + c + "'");
            }
        }

        return route;
    }

    private String nextToken() {
        char c;
        while (hasMore()) {
            c = next();

            switch (c) {
            case '.':
            case '/':
                back();
                if (routeTokenBuilder.length() == 0) {
                    throw new RouteException("Invalid URI: Empty path token not allowed");
                }

                return routeTokenBuilder.toString();
            default:
                routeTokenBuilder.append(c);
                break;
            }
        }

        throw new RouteException("Invalid URI: Missing file extension");
    }

    private String nextExtension() {
        while (hasMore()) {
            routeTokenBuilder.append(next());
        }

        return routeTokenBuilder.toString();
    }

    private boolean hasMore() {
        return routeURIIndex < routeURI.length();
    }

    private void back() {
        back(1);
    }

    private void back(final int offset) {
        routeURIIndex = Math.max(0, routeURIIndex - offset);
    }

    private char next() {
        if (!hasMore()) {
            throw new RouteException("Not enough characters left");
        }

        return routeURI.charAt(routeURIIndex++);
    }
}
